﻿using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Helpers
{
    public static class SeleniumEx
    {
        #region 封装WebDriver的操作
        /// <summary>
        /// 等待,ExpectedConditions
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="condition"></param>
        /// <param name="timeout"></param>
        public static void ExUntil<TResult>(this IWebDriver webDriver, Func<IWebDriver, TResult> condition, TimeSpan? timeout = null)
        {
            try
            {
                if (!timeout.HasValue)
                    timeout = TimeSpan.FromSeconds(10);
                WebDriverWait wait = new WebDriverWait(webDriver, timeout.Value);
                wait.Until(condition);
            }
            //catch (WebDriverTimeoutException)
            //{
            //}
            catch (Exception)
            {
                throw;
            }
        }
        /// <summary>
        /// 元素点击
        /// </summary>
        /// <param name="by"></param>
        public static IWebElement ExClick(this IWebDriver webDriver, By by, bool scrollFlag = true)
        {
            IWebElement el = null;
            try
            {
                el = webDriver.FindElement(by);
                //ExecuteScript("arguments[0].click();", el);
                var temp = scrollFlag ? "true" : "false";
                ExExecuteScript(webDriver, $"arguments[0].scrollIntoView({temp});", el);
                //ExExecuteScript(webDriver, "arguments[0].scrollIntoView(true);", el);
                ExUntil(webDriver, ExpectedConditions.ElementToBeClickable(by));
                try
                {
                    el.Click();
                }
                catch (WebDriverTimeoutException e)
                {
                    return el;
                }
                return el;
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        //public static void ExDispose(this IWebDriver webDriver)
        //{
        //    try
        //    {
        //        webDriver?.Dispose();
        //        webDriver = null;
        //    }
        //    catch (Exception)
        //    {
        //    }
        //}

        /// <summary>
        /// 元素点击
        /// </summary>
        /// <param name="by"></param>
        public static IWebElement ExClickByJs(this IWebDriver webDriver, By by)
        {
            IWebElement el = null;
            try
            {
                el = webDriver.FindElement(by);
                ExExecuteScript(webDriver, "arguments[0].click();", el);
                //ExExecuteScript(webDriver, "arguments[0].scrollIntoView(true);", el);
                //ExUntil(webDriver, ExpectedConditions.ElementToBeClickable(by));
                //el.Click();
                return el;
            }
            catch (WebDriverTimeoutException)
            {
                return el;
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public static IWebElement ExSubmit(this IWebDriver webDriver, By by)
        {
            IWebElement el = null;
            try
            {
                el = webDriver.FindElement(by);
                //ExecuteScript("arguments[0].click();", el);
                webDriver.ExExecuteScript("arguments[0].scrollIntoView(true);", el);
                ExUntil(webDriver, ExpectedConditions.ElementToBeClickable(by));
                el.Submit();
                return el;
            }
            catch (WebDriverTimeoutException)
            {
                return el;
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public static IWebElement ExSendKeys(this IWebDriver webDriver, By by, string text, bool clearBefore = true)
        {
            IWebElement el = null;
            try
            {
                el = webDriver.FindElement(by);
                //ExecuteScript("arguments[0].click();", el);
                ExUntil(webDriver, ExpectedConditions.ElementExists(by));
                if (clearBefore)
                    el.Clear();
                el.SendKeys(text);
                return el;
            }
            catch (WebDriverTimeoutException)
            {
                return el;
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public static bool ExIsFirefoxPageError(this IWebDriver webDriver)
        {
            //建立安全连接失败 or 页面载入出错
            return Regex.IsMatch(webDriver.PageSource, "&loadError.label;</title>")
                || Regex.IsMatch(webDriver.PageSource, "页面载入出错");
        }

        public static bool IsComplete(this IWebDriver webDriver)
        {
            string result = webDriver.ExExecuteScript("return document.readyState") as string;
            return result == "complete";
        }

        public static async Task WaitToComplete(this IWebDriver webDriver)
        {
            //await Task.Delay(1000);
            while (!webDriver.IsComplete())
            {
                await Task.Delay(100);
            }
        }

        /// <summary>
        /// 截图
        /// </summary>
        /// <param name="filename"></param>
        public static void ExSnapshot(this IWebDriver webDriver, string filePath)
        {
            ITakesScreenshot drivername = (ITakesScreenshot)webDriver;
            Screenshot scrFile = drivername.GetScreenshot();
            scrFile.SaveAsFile(filePath, ScreenshotImageFormat.Png);
        }


        /// <summary>
        /// 页面跳转
        /// </summary>
        /// <param name="url"></param>
        public static void ExGoToUrl(this IWebDriver webDriver, string url)
        {
            try
            {
                webDriver.Url = url;
                //WebDriver.Navigate().GoToUrl(url);
                //WebDriverWait wait = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(1000));
                //IAlert alert = wait.Until(ExpectedConditions.AlertIsPresent());
                //alert.SetAuthenticationCredentials("H9142YEPE0R727DD", "B96781AC19AC1926");
            }
            catch (WebDriverTimeoutException)
            {
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public static async Task ExWaitToUrl(this IWebDriver webDriver, params string[] urls)
        {
            int i = 20;
            while (i-- > 0)
            {
                if (webDriver.ExIsFirefoxPageError())
                    throw new Exception("IsFirefoxPageError");
                foreach (var url in urls)
                {
                    if (webDriver.Url.Contains(url))
                    {
                        await webDriver.WaitToComplete();
                        return;
                    }
                }
                await Task.Delay(500);
            }
            if (i <= 0)
                throw new Exception("timeout");
        }

        public static async Task ExWaitToUrl(this IWebDriver webDriver, int seconds, params string[] urls)
        {
            int i = seconds;
            while (i-- > 0)
            {
                if (webDriver.ExIsFirefoxPageError())
                    throw new Exception("IsFirefoxPageError");
                foreach (var url in urls)
                {
                    if (webDriver.Url.Contains(url))
                    {
                        await webDriver.WaitToComplete();
                        return;
                    }
                }
                await Task.Delay(1000);
            }
            if (i <= 0)
                throw new Exception("timeout");
        }


        public static object ExExecuteScript(this IWebDriver webDriver, string script, params object[] args)
        {
            try
            {
                IJavaScriptExecutor js = ((IJavaScriptExecutor)webDriver);
                return js.ExecuteScript(script, args);
            }
            catch (WebDriverTimeoutException)
            {
            }
            catch (Exception ex)
            {
                throw;
            }
            return null;
        }


        public static void ExDisposeDriver(this IWebDriver webDriver)
        {
            webDriver?.Dispose();
            //webDriver = null;
        }

        public static CookieContainer ExGetCookies(this IWebDriver webDriver)
        {
            CookieContainer cc = new CookieContainer();
            var allCookies = webDriver.Manage().Cookies.AllCookies;
            foreach (var cookie in allCookies)
            {
                System.Net.Cookie c = new System.Net.Cookie(cookie.Name, cookie.Value.Replace(",", "%2C"), cookie.Path, cookie.Domain);
                c.Expires = DateTime.Now.AddYears(1);
                cc.Add(c);
            }
            return cc;
        }

        public static List<System.Net.Cookie> ExGetCookiesList(this IWebDriver webDriver)
        {
            var result = new List<System.Net.Cookie>();
            var allCookies = webDriver.Manage().Cookies.AllCookies;
            foreach (var cookie in allCookies)
            {
                System.Net.Cookie c = new System.Net.Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain);
                //System.Net.Cookie c = new System.Net.Cookie(cookie.Name, cookie.Value.Replace(",", "%2C"), cookie.Path, cookie.Domain);
                c.Expires = DateTime.Now.AddYears(1);
                result.Add(c);
            }
            return result;
        }

        public static IWebDriver ExSetCookies(this IWebDriver webDriver, List<System.Net.Cookie> cookies)
        {
            if (cookies == null)
                throw new ArgumentNullException();
            foreach (var item in cookies)
            {
                webDriver.Manage().Cookies.DeleteCookieNamed(item.Name);
                webDriver.Manage().Cookies.AddCookie(new OpenQA.Selenium.Cookie(item.Name, item.Value, item.Domain, item.Path, item.Expires));
            }
            return webDriver;
        }

        public static string ExGetCookiesString(this IWebDriver webDriver)
        {
            string cc = string.Empty;
            var allCookies = webDriver.Manage().Cookies.AllCookies;
            foreach (var cookie in allCookies)
            {
                cc = cc + $"{cookie.Name}={cookie.Value.Replace(",", "%2C")};";
            }
            return cc;
        }

        #endregion
    }
}
